﻿/*
 *  Copyright 2009 Duck Pond, Inc.
 *  http://www.duckpondinc.com/
 *  
 *  This file is part of Server Watch & Alert.
 *
 *  Server Watch & Alert is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  Server Watch & Alert is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Server Watch & Alert.  If not, see <http://www.gnu.org/licenses/>.
 *  
 * */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Xml.Serialization;
using DuckPond.ServerWatchAlert.Core.TestActions;

namespace DuckPond.ServerWatchAlert.Core.ResultActions
{
    public class SendMailAction : ResultAction
    {
        /////////////////////
        // CONSTANTS
        /////////////
        public const short DefaultMinFailuresRequired = 2;


        /////////////////////
        // FIELDS
        /////////////
        /// <summary>
        /// Dictionary: TestName => AlertQueue
        /// </summary>
        private static Dictionary<string, LinkedList<TestResult>> _AlertHistory = new Dictionary<string, LinkedList<TestResult>>();
        protected short _MinFailuresRequired = DefaultMinFailuresRequired;


        /////////////////////
        // PROPERTIES
        /////////////
        [XmlAttribute("smtp-host")]
        public string SmtpServer { get; set; }

        [XmlAttribute("smtp-user")]
        public string SmtpUser { get; set; }

        [XmlAttribute("smtp-password")]
        public string SmtpPassword { get; set; }

        [XmlAttribute("from-email")]
        public string FromEmail { get; set; }

        [XmlAttribute("to-email")]
        public string ToEmail { get; set; }

        [XmlAttribute("min-failures-required")]
        public short MinFailuresRequired
        {
            get
            {
                return _MinFailuresRequired;
            }
            set
            {
                _MinFailuresRequired = value;
            }
        }

        /////////////////////
        // METHODS: overrides
        /////////////
        public override void PerformAction(TestResult result, IEnumerable<TestResult> resultHistory)
        {
            // grab the queue for this test name
            LinkedList<TestResult> actionHistory;
            if (!_AlertHistory.TryGetValue(result.Test.Name, out actionHistory))
            {
                _AlertHistory[result.Test.Name] = actionHistory = new LinkedList<TestResult>();
            }

            // remember the most recent status
            TestResult latestSentMailTestResult = null;
            if (actionHistory.Count > 0)
            {
                latestSentMailTestResult = actionHistory.Last.Value;
            }

            // if this is the first alert & its a failure
            if (latestSentMailTestResult == null)
            {
                actionHistory.AddLast(result);
                SendMail(result);
                return;
            }

            // is this test result same as the last
            if (latestSentMailTestResult.Success == result.Success)
            {
                return;
            }

            // is it too soon to send out another alert?
            TestAction test = result.Test;
            if (!(latestSentMailTestResult.TimeStamp.Add(test.MaxAlertRate) < DateTime.Now))
            {
                return;
            }

            // have we accumulated enough errors?
            if (result.Success != true && resultHistory.Reverse().TakeWhile(x => x.Success != true).Count() < MinFailuresRequired)
            {
                return;
            }

            // we've 
            actionHistory.AddLast(result);
            SendMail(result);

            while(actionHistory.Count > 4 * MinFailuresRequired)
            {
                actionHistory.RemoveFirst();
            }
        }


        /////////////////////
        // METHODS
        /////////////
        private void SendMail(TestResult result)
        {
            // prepare to email address
            MailAddress addressTo = new MailAddress(this.ToEmail);
            MailAddress addressFrom = new MailAddress(this.FromEmail);

            // prepare message
            MailMessage message = new MailMessage(addressFrom, addressTo);
            message.Subject = "Server Alert - \"" + result.Test.Name + "\" - " + (result.Success ? "Success" : "Failure");
            message.Body
                = "Server Alert - \"" + result.Test.Name + "\" - " + (result.Success ? "Success" : "Failure")
                + "\r\n\r\n"
                + result.Message;
            message.IsBodyHtml = false;

            //////
            // prepare client
            SmtpClient smtpClient;
            if (this.SmtpServer.Contains(":"))
            {
                string[] split = this.SmtpServer.Split(':');
                if (split.Length != 2)
                {
                    throw new Exception("Invalid SMTP hostname and/or port number.");
                }
                smtpClient = new SmtpClient(split[0], int.Parse(split[1]));
            }
            else
            {
                smtpClient = new SmtpClient(this.SmtpServer);
            }
            
            smtpClient.Credentials
                = new NetworkCredential(
                    this.SmtpUser,
                    this.SmtpPassword);

            // send message
            try
            {
                smtpClient.Send(message);
            }
            catch { }
        }
    }
}
